#include "BBox.h"

#include <limits>
using std::numeric_limits;


BBox::BBox(const Point3f &min_, const Point3f &max_)
{
    min.x = std::min(min_.x, max_.x);
    min.y = std::min(min_.y, max_.y);
    min.z = std::min(min_.z, max_.z);

    max.x = std::max(min_.x, max_.x);
    max.y = std::max(min_.y, max_.y);
    max.z = std::max(min_.z, max_.z);
}

BBox& BBox::doUnion(const Point3f &p)
{
    min.x = std::min(min.x, p.x);
    min.y = std::min(min.y, p.y);
    min.z = std::min(min.z, p.z);

    max.x = std::max(max.x, p.x);
    max.y = std::max(max.y, p.y);
    max.z = std::max(max.z, p.z);

    return *this;
}

bool BBox::intersect(const Ray& ray) const
{
    numeric_limits<float> limits = numeric_limits<float>();
    float tnear = limits.min() , tfar = limits.max();

    if(fabs(ray.d.x) < limits.epsilon())
    {
        if (ray.o.x < min.x || ray.o.x > max.x)
            return false;
    }
    else
    {
        float t1 = (min.x - ray.o.x) / ray.d.x; //(time at which ray intersects minimum X plane)
        float t2 = (max.x - ray.o.x) / ray.d.x; //(time at which ray intersects maximum X plane)
        if (t1 > t2)
        {
            float aux = t1;
            t1 = t2;
            t2 = aux;
        }
        if(t1 > tnear)
            tnear = t1;
        if(t2 < tfar)
            tfar = t2;
        if(tnear > tfar)
            return false;
        if(tfar < 0.0)
            return false;
    }

    if(fabs(ray.d.y) < limits.epsilon()){
        if (ray.o.y < min.y || ray.o.y > max.y)
            return false;
    }
    else
    {
        float t1 = (min.y - ray.o.y) / ray.d.y; //(time at which ray intersects minimum X plane)
        float t2 = (max.y - ray.o.y) / ray.d.y; //(time at which ray intersects maximum X plane)
        if (t1 > t2)
        {
            float aux = t1;
            t1 = t2;
            t2 = aux;
        }
        if(t1 > tnear)
            tnear = t1;
        if(t2 < tfar)
            tfar = t2;
        if(tnear > tfar)
            return false;
        if(tfar < 0.0)
            return false;
    }

    if(fabs(ray.d.z) < limits.epsilon()){
        if (ray.o.z < min.z || ray.o.z > max.z)
            return false;
    }
    else
    {
        float t1 = (min.z - ray.o.z) / ray.d.z; //(time at which ray intersects minimum X plane)
        float t2 = (max.z - ray.o.z) / ray.d.z; //(time at which ray intersects maximum X plane)
        if (t1 > t2)
        {
            float aux = t1;
            t1 = t2;
            t2 = aux;
        }
        if(t1 > tnear)
            tnear = t1;
        if(t2 < tfar)
            tfar = t2;
        if(tnear > tfar)
            return false;
        if(tfar < 0.0)
            return false;
    }

    return true;
}
